// CDN_WEB_SRV_Handler.h
//
// Copyright (c) 2011 LRUnit, LLC All Rights Reserved.
// Author : Kim McKinley
//
// This product includes software developed by :
//  LRUnit, LLC
//  Sqlite (http://www.sqlite.org)
//
// This software is under the terms of the MIT License :
/*
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.
*/


// **********************************************************************

#if !defined(CDN_WEB_SRV_Handler_h)
#define CDN_WEB_SRV_Handler_h

#define MAX_WEB_RECV_LEN 8192

// Forward declare of our friend Acceptor that gives us the HTTP request
namespace cdn { namespace net { namespace tcp { class Acceptor; } } }

namespace cdn {
  namespace web {
    namespace srv {

// **********************************************************************
/// HTTP message handler 
/**
  HTTP request handler manages HTTP GET, HEAD, and POST requests.
  The handler delegates the real work to 
  The request is parsed and handed off to 'server extensions' to process
   and format a reply. If none of the extensions can build the
   reply - a 'Not Found' reply is returned to the requestor.
  The handler also keeps track of how long the connection has been active and
   has been waiting for a request - not going to wait forever, so disconnects
   if waiting too long.
*/
class Handler : public os::Event
{
protected:
  // The network interface that delivers requests and replies
  friend class net::tcp::Acceptor;

  // These are filled in by our buddy - the Acceptor
  SOCKET clientSocket_;                 ///< Client socket that the request was received on
  struct sockaddr_in clientAddress_;    ///< Address of the client requesting data
  int clientAddressLen_;                ///< Length of clientAddress

  // HTTP message to be processed
  char data[MAX_WEB_RECV_LEN];
  int datasize;

  int requestCount_;                    ///< Number of requests handled by this handler thread

public:
  /// Constructs a handler for HTTP requests
  Handler() 
  {
    clientSocket_ = NULL;
    clientAddressLen_ = sizeof(struct sockaddr_in);
    requestCount_ = 0;
    memset(data,0,MAX_WEB_RECV_LEN);
  }

  // Insure the our event thread shuts down
  virtual ~Handler() { SignalShutdown(); }

// **********************************************************************
  // Event handlers

  /// Keep-alive has timed out for this socket - so close the socket
  //   (The Acceptor will feel the pain and get a winsock error 10053
  //    resulting in terminating this connection, destroying me in
  //    the process!)
  virtual bool Timeout()
  {
    closesocket(clientSocket_);
    return true;
  }

  /// Process the request
  //  Signaled by the Acceptor when data has been received for processing
  virtual void ProcessRequest()
  {
    // Assume the server extensions are unable to satisfy the request
    int result = -1;

    // Breaks down the request components so are accessable
    //  via STL, also has a place to store the reply
    msg::Http http;

    // Extract the Path and Parameters from the URL
    // If an invalid URL then tell the browser is a bad request
    // Closing the socket will result in (my boss) the Acceptor to disconnect
    if ( !http.ParseHttpMessage(data) )
    {
      http.content() << "<html><body>Bad or Unknown Request Type - use GET HEAD or POST</body></html>";
      http.contentType("text/html");
      Reply(400, http);
      closesocket(clientSocket_);
      return;
    }

    // Process the request using server extension classes
    //  If you create a new server extension - this is where to add it to be called
    //  Programming Note: these 'if's should technically be indented - but that looked ugly
    //   Reason for nested ifs is so extensions that are not needed to process the
    //   request are not created (instantiated) - saving a bit of memory/processing time

    // See if request is for a file
    FileContent pai(http);
    if ( (result = pai.BuildContent()) == -1 )
    { // See if request is for a XML document of SQL command(s) results
    SQLiteXml xml(http);
    if ( (result = xml.BuildContent())  == -1 )
    { // See if request is for a HTML table of SQL command(s) results
    SQLiteHtml htm(http);
    if ( (result = htm.BuildContent()) == -1 )
    { // Show the console window - for when user previously commanded it to hide
    ShowConsole scw(http);
    if ( (result = scw.BuildContent()) == -1 )
    {

            // Place next server extension here

    } // ShowConsole
    } // SQLiteHtml
    } // SQLiteXml
    } // FileContent

    // One of the server extensions processed the request
    if ( result != -1 )
      Reply(result, http);
    else // None of the server extensions could not satisfy the request :(
      Reply(404, http);
 
    // Number of requests this handler - handled. Can be handy for debugging
    requestCount_++;
    #ifdef _DEBUG
      std::cout << requestCount_ << ".";
    #endif
  }

// **********************************************************************
  /// Construct HTTP reply and send to the requestor
  void Reply(int error, msg::Http &http)
  {
    std::string errorString;

    // Common errors that we might respond to browser
    switch( error )
    {
      case 200: errorString = "OK"; break;
      case 304: errorString = "Not Modified"; break;
      case 400: errorString = "Bad Request"; break;
      case 401: errorString = "Unauthorized"; break;
      case 403: errorString = "Forbidden"; break;
      case 404: errorString = "Not Found"; break;
      case 408: errorString = "Request Timeout"; break;
      case 501: errorString = "Not Implemented"; break;
      case 503: errorString = "Service Unavailable"; break;
      default: errorString = "Unknown";
      }

    // Get length of the content
    size_t bodylength = http.content().tellp();
    if (bodylength == -1) bodylength = 0; // if tellp() failed

    // Start building the response by making the header
    std::stringstream response;
    response << "HTTP/1.1 " << error << " " << errorString << "\r\n" << 
      "Server: " << gConfig["DEFAULT"]["SERVER_NAME"] << "\r\n" <<
      "Date: " << http.Rfc1123_DateTime() << "\r\n" <<
      "Content-Length: " << bodylength << "\r\n";
      // Headers 
      std::map< std::string, std::string >::const_iterator it;
      for ( it = http.rsp.headers_.begin(); it != http.rsp.headers_.end(); ++it )
        response << it->first << ": " << it->second << "\r\n";

      response << "Connection: keep-alive" << "\r\n\r\n";

    // Get the length of the header
    int hdrlength = response.tellp();

    // Allocate a buffer big enough to hold both the header and content
    char *reply = new char[hdrlength + bodylength];

    // Put the header and content in the reply
    response.read(reply, hdrlength);
    http.content().read(reply + hdrlength, bodylength);

    // Send the reply
    send(clientSocket_, reply ,(int)hdrlength + bodylength, NULL);

    delete[] reply;
    return;
  }

}; // class Handler
// **********************************************************************

} // namespace srv
} // namespace web
} // namespace cdn


#endif
